﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Leek.Persistence
{
    public sealed class DomainContext
    {
        private readonly Dictionary<Type, Dictionary<object, IDomainObject>> _instanceTypes;


        public DomainContext()
        {
            _instanceTypes = new Dictionary<Type, Dictionary<object, IDomainObject>>();
        }


        public void PersistChanges()
        {
            foreach (var instance in _instanceTypes.Values.SelectMany(instances => instances.Values))
            {
                instance.Persist();
            }
        }


        internal IQueryable<T> Query<T>()
            where T : IDomainObject
        {
            var type = typeof(T);

            Dictionary<object, IDomainObject> instances;
            if (_instanceTypes.TryGetValue(type, out instances))
            {
                return instances.Values.Cast<T>().AsQueryable();
            }
            else
            {
                return new T[] { }.AsQueryable();
            }
        }

        //internal DomainObject<T, TId> Get<T, TId>(DomainObjectId<T, TId> id)
        //    where T : DomainObject<T, TId>
        //{
        //    var type = typeof(T);

        //    Dictionary<object, IDomainObject> instances;
        //    if (_instanceTypes.TryGetValue(type, out instances))
        //    {
        //        IDomainObject instance;
        //        if (instances.TryGetValue(id, out instance) && instance is T)
        //        {
        //            return (T)instance;
        //        }
        //        else
        //        {
        //            throw new KeyNotFoundException(String.Format("No domain object of type {0} with id {1} has been registered.", id));
        //        }
        //    }
        //    else
        //    {
        //        throw new KeyNotFoundException(String.Format("No domain objects of type {0} have been registered.", type));
        //    }
        //}

        internal bool ContainsId<T, TId>(DomainObjectId<T, TId> id)
            where T : DomainObject<T, TId>
        {
            Dictionary<object, IDomainObject> instances;
            return _instanceTypes.TryGetValue(typeof(T), out instances) &&
                instances.ContainsKey(id);
        }

        internal bool TryGet<T, TId>(DomainObjectId<T, TId> id, out T instance) //TODO: Assert that the ID context is the same as 'this'
            where T : DomainObject<T, TId>
        {
            instance = default(T);

            var type = typeof(T);

            Dictionary<object, IDomainObject> instances;
            if (_instanceTypes.TryGetValue(type, out instances))
            {
                IDomainObject val;
                if (instances.TryGetValue(id, out val) && val is T)
                {
                    instance = (T)val;
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        internal void Register<T, TId>(DomainObject<T, TId> domainObject)
            where T : DomainObject<T, TId>
        {
            var type = typeof(T);
            var id = domainObject.Id;

            Dictionary<object, IDomainObject> instances;
            if (_instanceTypes.TryGetValue(type, out instances))
            {
                instances[id] = domainObject;
            }
            else
            {
                instances = new Dictionary<object, IDomainObject>();
                instances[id] = domainObject;
                _instanceTypes[type] = instances;
                return;
            }
        }
    }
}
